自定义指令 has:扩展更灵活通用的权限指令
概述
v-hasPermission 指令硬编码了从 userStore.roles 读取角色数据。如果项目中的权限字段名是 permission 或 access 而非 roles,就需要重写指令。本节通过 Vue 指令的修饰符(modifiers)机制,实现一个通用的 v-has 指令,支持从不同的 store 字段读取权限数据。
v-hasPermission 的局限性
<!-- v-hasPermission 只能读取 store.roles -->
<el-button v-hasPermission="['admin']">编辑</el-button>
<!-- 如果权限数据存在 store.permissions 中,就无法使用 -->
<!-- 如果权限数据存在 store.access 中,也无法使用 -->
html
v-has 通用指令实现
// directives/modules/has.ts
import type { Directive, DirectiveBinding } from 'vue'
import { useUserStore } from '@/stores/user'
/**
* v-has 指令 — 通用权限控制
*
* 通过修饰符指定从 store 中读取的字段:
* v-has.role="['admin']" → 读取 store.roles
* v-has.permission="['edit']" → 读取 store.permissions
* v-has.access="['write']" → 读取 store.access
*
* 特殊修饰符:
* v-has.role.not="['admin']" → 取反(无 admin 角色时显示)
*/
const has: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding<string[]>) {
const { value, modifiers } = binding
const userStore = useUserStore()
if (!value || !Array.isArray(value) || value.length === 0) {
return
}
// 从 modifiers 中提取属性名(排除 .not)
const keys = Object.keys(modifiers)
const attribute = keys.find(key => key !== 'not') || 'roles'
const isNot = modifiers.not || false
// 从 store 中动态读取对应字段
const userPermissions = (userStore as any)[attribute] || []
// 权限检查
const hasAuth = checkPermission(userPermissions, value)
const shouldShow = isNot ? !hasAuth : hasAuth
if (!shouldShow) {
el.parentNode?.removeChild(el)
}
}
}
function checkPermission(userPerms: string[], requiredPerms: string[]): boolean {
// admin 角色默认拥有所有权限
if (userPerms.includes('admin')) return true
return requiredPerms.some(perm => userPerms.includes(perm))
}
export default has
typescript
使用方式
<template>
<!-- 从 store.roles 读取 -->
<el-button v-has.role="['admin']">管理员按钮</el-button>
<!-- 从 store.permissions 读取 -->
<el-button v-has.permission="['system:user:edit']">编辑用户</el-button>
<!-- 从 store.access 读取 -->
<el-button v-has.access="['write']">写入按钮</el-button>
<!-- 取反修饰符 -->
<el-button v-has.role.not="['admin']">非管理员可见</el-button>
<!-- 默认读取 roles -->
<el-button v-has="['editor']">编辑可见</el-button>
</template>
vue
修饰符机制详解
// binding.modifiers 是一个对象,键名为修饰符名称
// v-has.role.not="['admin']"
binding.modifiers === {
role: true, // 从 store.roles 读取
not: true // 取反
}
// 提取属性名(排除 not)
const keys = Object.keys(modifiers) // ['role', 'not']
const attribute = keys.find(k => k !== 'not') // 'role'
const isNot = modifiers.not // true
typescript
v-has vs v-hasPermission 对比
| 维度 | v-hasPermission | v-has |
|---|---|---|
| 数据源 | 固定 store.roles | 通过修饰符动态指定 |
| 灵活性 | 低 | 高 |
| 适用场景 | 仅角色权限 | 角色/权限/访问控制均可 |
| 取反 | 支持 .not | 支持 .not |
| 默认行为 | 读取 roles | 读取 roles |
store 数据结构支持
// stores/user.ts — 支持多种权限字段
interface UserState {
roles: string[] // 角色列表 ['admin', 'editor']
permissions: string[] // 权限列表 ['system:user:add', 'system:menu:edit']
access: string[] // 访问控制 ['read', 'write', 'delete']
}
typescript
实践要点
v-has通过修饰符动态指定从 store 读取哪个字段,替代v-hasPermission的硬编码Object.keys(binding.modifiers)获取所有修饰符,排除.not后即为属性名- 默认属性为
roles,与v-hasPermission行为一致 .not修饰符实现取反,与v-hasPermission.not用法相同- 一个
v-has指令覆盖角色、权限、访问控制等多种场景
↑